package com.github.data_manage.controller

import com.github.data_manage.common.CommonResult
import com.github.data_manage.config.FUND_NO_DATA_EXCEPTION
import com.github.data_manage.config.SystemException
import com.github.data_manage.controller.FundController.Companion.importingDataFundIds
import com.github.data_manage.dao.*
import com.github.data_manage.domain.po.FundBusinessData
import com.github.data_manage.domain.po.FundDepthResearch
import com.github.data_manage.domain.vo.*
import com.github.data_manage.service.ContrastService
import com.github.data_manage.service.FundDeepResearchService
import com.github.data_manage.service.FundService
import com.github.data_manage.util.CollectionExtensions.containsAny
import com.github.data_manage.util.DateTimeUtils.Companion.atEndOfMonth
import com.github.data_manage.util.mapAsync
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.tags.Tag
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.runBlocking
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.data.repository.findByIdOrNull
import org.springframework.transaction.support.TransactionTemplate
import org.springframework.web.bind.annotation.*

/**
 * @author chenhainan
 * @since 2025-02-15 17:15
 */
@RestController
@Tag(name = "FundAttachController", description = "基金数据管理")
@RequestMapping("/funds")
class FundAttachController(
    private val fundService: FundService,
    private val contrastService: ContrastService,
    private val fundBusinessDataDao: FundBusinessDataDao,
    private val fundDao: FundDao,
    private val transactionTemplate: TransactionTemplate,
    private val fundDepthResearchService: FundDeepResearchService,
    private val contrastDao: ContrastDao,
    private val contrastBusinessDataDao: ContrastBusinessDataDao
) {

    companion object {

        private val log: Logger = LoggerFactory.getLogger(FundController::class.java)

    }

    @PostMapping("/histories")
    @Operation(description = "批量查询历史业绩")
    fun fundHistoryNavs(@RequestBody batchFundDataQueryVo: BatchFundDataQueryVo): CommonResult<List<FundMonthInfoVo>?> {
        if (importingDataFundIds.containsAny(batchFundDataQueryVo.fundIds)) {
            return CommonResult.failed("数据后台处理中,请稍后再操作")
        }
        val baseFund =
            fundDao.findById(batchFundDataQueryVo.baseFundId).orElseThrow {
                SystemException(
                    "基准基金信息不存在",
                    FUND_NO_DATA_EXCEPTION
                )
            }
        val (fundValueMinDate, fundValueMaxDate) = fundService.fundDataTimeRange(batchFundDataQueryVo.baseFundId)

        val navData = runBlocking {
            val baseFundNav = async {
                val fundAllBusinessData = fundBusinessDataDao.findAllByFundIdAndTimeBetween(
                    batchFundDataQueryVo.baseFundId,
                    fundValueMinDate,
                    fundValueMaxDate
                )
                val fundAllNavData =
                    fundService.calcFundNavData(baseFund, fundAllBusinessData).sortedBy { it.time }

                FundMonthInfoVo(
                    baseFund.fundId!!,
                    baseFund.fundName!!,
                    1,
                    fundAllNavData.map { fundNavData ->
                        MonthInfoVo(fundNavData.value, fundNavData.time!!)
                    }
                )
            }
            val fundReturns = batchFundDataQueryVo.fundIds.mapAsync { fundId ->
                val fund = fundDao.findById(fundId).orElse(null) ?: return@mapAsync null
                val fundAllBusinessData = fundBusinessDataDao.findAllByFundIdAndTimeBetween(
                    fundId,
                    fundValueMinDate,
                    fundValueMaxDate
                )
                val fundAllNavData =
                    fundService.calcFundNavData(fund, fundAllBusinessData).sortedBy { it.time }

                FundMonthInfoVo(
                    fund.fundId!!,
                    fund.fundName!!,
                    1,
                    fundAllNavData.map { fundNavData ->
                        MonthInfoVo(fundNavData.value, fundNavData.time!!)
                    }
                )
            }

            val contrastReturns = batchFundDataQueryVo.contrastIds.mapAsync { contrastId ->
                val contrast = contrastDao.findById(contrastId).orElse(null) ?: return@mapAsync null
                val contrastAllBusinessData = contrastBusinessDataDao.findAllByContrastIdAndTimeBetween(
                    contrastId,
                    fundValueMinDate,
                    fundValueMaxDate
                )
                val contrastAllNavData =
                    contrastService.calcContrastNavData(contrast, contrastAllBusinessData).sortedBy { it.time }

                FundMonthInfoVo(
                    contrast.id!!,
                    contrast.name,
                    1,
                    contrastAllNavData.map { fundReturnData ->
                        MonthInfoVo(fundReturnData.value, fundReturnData.time!!)
                    }
                )
            }

            return@runBlocking listOf(
                listOf(baseFundNav.await()),
                fundReturns.awaitAll().filterNotNull(),
                contrastReturns.awaitAll().filterNotNull()
            ).flatten()
        }
        return CommonResult.success(navData)
    }

    @Operation(description = "批量查询回撤分析")
    @PostMapping(value = ["/returns"])
    fun fundReturns(@RequestBody batchFundDataQueryVo: BatchFundDataQueryVo): CommonResult<List<FundMonthInfoVo>?> {
        if (importingDataFundIds.containsAny(batchFundDataQueryVo.fundIds)) {
            return CommonResult.failed("数据后台处理中,请稍后再操作")
        }
        val baseFund =
            fundDao.findById(batchFundDataQueryVo.baseFundId).orElseThrow { SystemException("基准基金信息不存在") }
        val (fundValueMinDate, fundValueMaxDate) = fundService.fundDataTimeRange(batchFundDataQueryVo.baseFundId)
        val returnData = runBlocking {
            val baseFundReturn = async {
                val fundAllBusinessData = fundBusinessDataDao.findAllByFundIdAndTimeBetween(
                    batchFundDataQueryVo.baseFundId,
                    fundValueMinDate,
                    fundValueMaxDate
                )
                val fundAllReturnData =
                    fundService.calcFundReturnData(baseFund, fundAllBusinessData).sortedBy { it.time }

                FundMonthInfoVo(
                    baseFund.fundId!!,
                    baseFund.fundName!!,
                    1,
                    fundAllReturnData.map { fundReturnData ->
                        MonthInfoVo(fundReturnData.value, fundReturnData.time!!)
                    }
                )
            }
            val fundReturns = batchFundDataQueryVo.fundIds.mapAsync { fundId ->
                val fund = fundDao.findById(fundId).orElse(null) ?: return@mapAsync null
                val fundAllBusinessData = fundBusinessDataDao.findAllByFundIdAndTimeBetween(
                    fundId,
                    fundValueMinDate,
                    fundValueMaxDate
                )
                val fundAllReturnData =
                    fundService.calcFundReturnData(fund, fundAllBusinessData).sortedBy { it.time }

                FundMonthInfoVo(
                    fund.fundId!!,
                    fund.fundName!!,
                    1,
                    fundAllReturnData.map { fundReturnData ->
                        MonthInfoVo(fundReturnData.value, fundReturnData.time!!)
                    }
                )
            }

            val contrastReturns = batchFundDataQueryVo.contrastIds.mapAsync { contrastId ->
                val contrast = contrastDao.findById(contrastId).orElse(null) ?: return@mapAsync null
                val contrastAllBusinessData = contrastBusinessDataDao.findAllByContrastIdAndTimeBetween(
                    contrastId,
                    fundValueMinDate,
                    fundValueMaxDate
                )
                val contrastAllReturnData =
                    contrastService.calcContrastReturnData(contrast, contrastAllBusinessData).sortedBy { it.time }

                FundMonthInfoVo(
                    contrast.id!!,
                    contrast.name,
                    1,
                    contrastAllReturnData.map { fundReturnData ->
                        MonthInfoVo(fundReturnData.value, fundReturnData.time!!)
                    }
                )
            }

            return@runBlocking listOf(
                listOf(baseFundReturn.await()),
                fundReturns.awaitAll().filterNotNull(),
                contrastReturns.awaitAll().filterNotNull()
            ).flatten()
        }

        return CommonResult.success(returnData)
    }


    @Operation(description = "批量进行基金数据分析")
    @PostMapping(value = ["/batchAnalysis"])
    fun fundAnalysis(@RequestBody batchFundDataQueryVo: BatchFundDataQueryVo): CommonResult<BatchFundAnalysisVo?> {
        if (importingDataFundIds.containsAny(batchFundDataQueryVo.fundIds)) {
            return CommonResult.failed("数据后台处理中,请稍后再操作")
        }
        fundDao.findById(batchFundDataQueryVo.baseFundId).orElseThrow { SystemException("基准基金信息不存在") }
        val (fundValueMinDate, fundValueMaxDate) = fundService.fundDataTimeRange(batchFundDataQueryVo.baseFundId)
        return runBlocking {

            val baseFundAnalysisVo =
                fundService.fundAnalysis(batchFundDataQueryVo.baseFundId, fundValueMinDate, fundValueMaxDate)

            val fundTask = batchFundDataQueryVo.fundIds.mapAsync { fundId ->
                fundService.fundAnalysis(
                    fundId, fundValueMinDate, fundValueMaxDate, baseFundAnalysisVo.allReturnData
                )
            }
            val contrastTask = batchFundDataQueryVo.contrastIds.mapAsync { contrastId ->
                contrastService.contrastAnalysis(
                    contrastId, fundValueMinDate, fundValueMaxDate, baseFundAnalysisVo.allReturnData
                )
            }
            return@runBlocking CommonResult.success(
                BatchFundAnalysisVo(
                    baseFundAnalysisVo,
                    fundTask.awaitAll(),
                    contrastTask.awaitAll()
                )
            )
        }
    }

    @Operation(description = "修改月度表现")
    @PutMapping(value = ["/modifyMonthlyManifestation"])
    fun modifyMonthAnalysis(@RequestBody modifyMonthAnalysisVo: ModifyMonthAnalysisVo): CommonResult<Boolean?> {
        val fundId = modifyMonthAnalysisVo.fundId!!
        val fund = fundDao.findByIdOrNull(fundId) ?: return CommonResult.failed("基金未找到")
        transactionTemplate.executeWithoutResult {
//            if (modifyMonthAnalysisVo.fundBusinessValues.isEmpty()) {
//
//                return@executeWithoutResult
//            }
            fundBusinessDataDao.deleteAllByFundId(fundId)
            modifyMonthAnalysisVo.fundBusinessValues.forEach { fundBusinessValue ->
                val dbFundBusinessData = FundBusinessData(
                    fundId,
                    fund.fundName!!,
                    fundBusinessValue.time.atEndOfMonth(),
                    fundBusinessValue.value
                )
                dbFundBusinessData.value = fundBusinessValue.value
                fundBusinessDataDao.save(dbFundBusinessData)
            }
        }
        Thread.startVirtualThread {
            importingDataFundIds.add(fundId)
            // 计算 nav
            fundService.calcFundNavData(fundId)
            // 计算回撤
            fundService.calcFundReturnData(fundId)
            // 基金年化数据
            try {
                val (yearNav, yearStandardDeviation) = fundService.fundYearInfo(fundId)
                fundDao.findById(fundId).ifPresent { fund ->
                    fund.yearNav = yearNav
                    fund.yearStandardDeviation = yearStandardDeviation
                    fundDao.save(fund)
                    val fundDepthResearch =
                        fundDepthResearchService.findByFundId(fund.fundId!!) ?: FundDepthResearch(fund.fundId!!)
                    fundDepthResearchService.handleDepthResearchInfo(fund, fundDepthResearch)
                }


            } catch (e: Exception) {
                log.error("计算基金[{}]的年化数据发生错误", fundId, e)
            }
            importingDataFundIds.remove(fundId)
        }

        return CommonResult.success(data = true, "修改成功")
    }

}
